package blackstar.agent.jvm;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import blackstar.agent.AgentConfig;
import blackstar.agent.event.Event;
import blackstar.agent.event.EventDispatcherSupport;
import blackstar.agent.event.EventListener;
import blackstar.agent.event.ShutdownEvent;
import blackstar.agent.event.StartupEvent;
import blackstar.agent.jmx.MBeanUtils;
import blackstar.agent.schedule.ScheduleEvent;
import blackstar.agent.schedule.ScheduleTask;
import blackstar.agent.service.ServiceFactory;

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

/**
 * @author raywu (ayufox@gmail.com)
 * 
 */
public class LocalJVMManager extends EventDispatcherSupport implements
		LocalJVMManagerMBean, JMXProxyManager, EventListener
{
	private final static Log LOGGER = LogFactory.getLog(LocalJVMManager.class);

	public final static String BEAN_NAME = "JVMManager";

	private Map<String, LocalJVM> jvms = new HashMap<String, LocalJVM>();
	private boolean isStart = false;
	private String refreshCronExpression = "0 0/1 * * * ?";

	public LocalJVMManager()
	{
		ServiceFactory.registJMXProxyManager(this);
	}

	public Class[] events()
	{
		return new Class[]
		{ StartupEvent.class, ShutdownEvent.class, JMXProxyErrorEvent.class };
	}

	public void onEvent(Event event) throws Exception
	{
		if (event instanceof StartupEvent)
		{
			startup();
		} else if (event instanceof ShutdownEvent)
		{
			shutdown();
		} else if (event instanceof JMXProxyErrorEvent)
		{
			stopLocalJVM(((JMXProxyErrorEvent) event).getJMXProxy().getPid());
		} else
		{
			LOGGER.warn("unhandle event : " + event);
		}
	}

	public synchronized void startup()
	{
		if (this.isStart)
		{
			LOGGER.info("LocalJVMManager had been started.");
			return;
		}
		
		String refreshCronExpression = AgentConfig.getProperty("refreshCronExpression");
		if (refreshCronExpression != null && refreshCronExpression.length() > 0)
		{
			this.refreshCronExpression = refreshCronExpression;
		}

		try
		{
			MBeanUtils.registMBean(BEAN_NAME, this);

			refresh();

		} catch (Exception e)
		{
			LOGGER.error("Start manager failed", e);

			return;
		}

		LOGGER.info("start manager success");

		this.isStart = true;

		if (this.refreshCronExpression != null)
		{
			dispatch(new ScheduleEvent(this.refreshCronExpression,
					new ScheduleTask()
					{
						public void schedule()
						{
							refresh();
						}
					}));
		}
		
	}

	public void shutdown()
	{
		if (!this.isStart)
		{
			LOGGER.info("LocalJVMManager had been stoped.");
			return;
		}
		
		clear();

		LOGGER.info("stop manager success");

		this.isStart = false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see blackstar.agent.LocalJVMManagerMBean#stop(java.lang.String)
	 */
	public synchronized void stopLocalJVM(String pid)
	{
		LocalJVM localJVM = jvms.get(pid);
		if (localJVM != null)
		{
			stopLocalJVM(localJVM);
			jvms.remove(localJVM.getPid());
		}
		LOGGER.info("stop localjvm[" + pid + "] success");
	}

	protected synchronized void stopLocalJVM(LocalJVM localJVM)
	{
		dispatch(new JMXProxyShutdownEvent(localJVM));
		localJVM.stop();
	}

	protected synchronized boolean startupLocalJVM(LocalJVM localJVM)
	{
		if (!localJVM.start())
		{
			return false;
		}
		jvms.put(localJVM.getPid(), localJVM);

		dispatch(new JMXProxyStartupEvent(localJVM));

		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see blackstar.agent.LocalJVMManagerMBean#clear()
	 */
	public synchronized void clear()
	{
		for (LocalJVM localJVM : jvms.values())
		{
			stopLocalJVM(localJVM);
		}

		jvms.clear();

		LOGGER.info("clear success");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see blackstar.agent.LocalJVMManagerMBean#refresh()
	 */
	public synchronized void refresh()
	{
		LOGGER.info("start refresh");
		List<VirtualMachineDescriptor> list = VirtualMachine.list();
		Set<String> pids = new HashSet<String>();
		for (VirtualMachineDescriptor desc : list)
		{
			pids.add(desc.id());
			if (!jvms.containsKey(desc.id()))
			{
				LocalJVM localJVM = new LocalJVM(desc.id(), desc.displayName());
				startupLocalJVM(localJVM);
			}
		}

		List<String> deleteIds = new ArrayList<String>();

		deleteIds.clear();
		for (String pid : jvms.keySet())
		{
			if (!pids.contains(pid))
			{
				deleteIds.add(pid);
			}
		}
		for (String pid : deleteIds)
		{
			stopLocalJVM(pid);
		}

		LOGGER.info("start success");
	}

	public void setRefreshCronExpression(String refreshCronExpression)
	{
		this.refreshCronExpression = refreshCronExpression;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see blackstar.agent.jvm.JMXProxyManager#getProxy(java.lang.String)
	 */
	public JMXProxy getProxy(String pid)
	{
		return this.jvms.get(pid);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see blackstar.agent.jvm.JMXProxyManager#listProxys()
	 */
	public Collection<JMXProxy> listProxys()
	{
		Collection<JMXProxy> list = new ArrayList<JMXProxy>();
		for (LocalJVM jvm : this.jvms.values())
		{
			list.add(jvm);
		}
		return list;
	}
}